import wx
import math 
from log_object import *

def makeNew(parent, window, param1):
    """ this is how we instantiate the device object from
        the main program that loads this plugin """
    return SpaceGraph(parent, window, param1)

class SpaceGraph:
	def __init__(self, parent, window, param1):
		self.parent = parent
		self.window = window

		self.type = SPACEGRAPH 
		self.param1 = param1
		self.param2 = NOT_DEFINED 

	def draw(self, dc, start, end, dim, space):
		active_gap =  self.parent.activeTime
		log_date = ""

		foreground = self.parent.getForegroundColor()
		background = self.parent.getBackgroundColor()

		dc.SetBrush(wx.TRANSPARENT_BRUSH)
		dc.SetPen(wx.Pen(foreground, 1))
		dc.SetTextBackground(background)
		dc.SetTextForeground(foreground)
		font = wx.Font(15, wx.TELETYPE, wx.NORMAL, wx.BOLD)
		dc.SetFont(font)

		base_unit = 10
		window_width = int(self.window.Width / space[0])
		window_height = int(self.window.Height / space[1])

		# draw vertical line & information
		x = (dim[0] * window_width) + 30
		origin_x = x
		y = (dim[1] * window_height) + window_height -61
		dc.DrawLines(((x, dim[1] * window_height), (x, y)))
		right_gap = 50
		graph_width = window_width - right_gap 
		endx = (dim[0] * window_width) + graph_width 
		dc.DrawText("time (unit: min.) ->", window_width - 190, y+30)

		dc.DrawText("used display space", origin_x + 5, 10)

		font = wx.Font(12, wx.TELETYPE, wx.NORMAL, wx.BOLD)
		dc.SetFont(font)
		log_date = ''
		for current_log in self.parent.log_list :
			log_date = current_log.date
			break
		dc.DrawText(log_date, origin_x + graph_width -100, 10)

 		y_start = dim[1] * window_height 

		space_used = []
		for current_log in self.parent.log_list :
			if end == -1 or end > current_log.duration:
				end = current_log.duration
			self.display_width = current_log.display_width
			self.display_height = current_log.display_height

			idx = 0
			for window in current_log.window_list: 
				if idx == 0 : 
					# fill first ...
  					for log in window.log_list:
                        			if  log.mode == LOG_WINDOW_NEW or log.mode == LOG_WINDOW_CHANGE :
							temp_list = []
							temp = (window.name, log.left, log.right, log.bottom, log.top)
							temp_list.append(temp)
							l = (log.time, temp_list)	
							space_used.append(l)
				else : 
					# from second...
					remove_flag = False
  					for log in window.log_list:
                        			if  log.mode == LOG_WINDOW_NEW or log.mode == LOG_WINDOW_CHANGE :
							# check space_used.... space_used is sorted... by time 
							added_flag = False
							sub_idx = 0 
							prev_space = None 
							for space in space_used : 
								if log.time == space[0] :
									l = space[1] 
									temp = (window.name, log.left, log.right, log.bottom, log.top)
									l.append(temp)
									added_flag = True
									break
								if prev_space != None :
									if log.time > prev_space[0] and log.time < space[0] :
										# copy previous space info
										prev_list = prev_space[1]
										temp_list = []
										for temp in prev_list :
											if temp[0] != window.name : 
												temp_list.append(temp) 
										temp = (window.name, log.left, log.right, log.bottom, log.top)
										temp_list.append(temp)

										l = (log.time, temp_list)	
										space_used.insert(sub_idx, l)
										added_flag = True
										break
								prev_space = space
								sub_idx += 1 

							if added_flag == False :
								temp_list = []
								temp = (window.name, log.left, log.right, log.bottom, log.top)
								temp_list.append(temp)
								l = (log.time, temp_list)	
								space_used.append(l)
						elif log.mode ==  LOG_WINDOW_REMOVE :
							remove_flag = True 
							# add...
							# check space_used.... space_used is sorted... by time 
							prev_space = None 
							sub_idx = 0 
							for space in space_used : 
								if prev_space != None :
									if log.time > prev_space[0] and log.time < space[0] :
										prev_list = prev_space[1]
										temp_list = []
										for temp in prev_list :
											if temp[0] != window.name : 
												temp_list.append(temp) 
										l = (log.time, temp_list)	
										space_used.insert(sub_idx, l)
										break
								prev_space = space
								sub_idx += 1 
							# refinement
							prev_space = None 
							for refine_idx in range(sub_idx) : 
								space = space_used[refine_idx]
								temp_list = space[1] 
								found = False
								for temp in temp_list :
									if temp[0] == window.name : 
										prev_space = temp 
										found = True 
										break
								if found == False :
									if prev_space != None :
										temp_list.append(prev_space)
							break
					if remove_flag == False : 
						#find sub_idx 
						max = len(space_used)
						sub_idx = -1 
						for refine_idx in range(max) : 
							temp_idx = max - refine_idx -1
							space = space_used[temp_idx]
							temp_list = space[1] 
							found = False
							for temp in temp_list :
								if temp[0] == window.name : 
									sub_idx = temp_idx 
									break
							if sub_idx != -1 : 
								break
						if sub_idx == -1 : 
							sub_idx = 0

						# refinement
						prev_space = None 
						for refine_idx in range(sub_idx) : 
							space = space_used[refine_idx]
							temp_list = space[1] 
							found = False
							for temp in temp_list :
								if temp[0] == window.name : 
									prev_space = temp 
									found = True 
									break
							if found == False :
								if prev_space != None :
									temp_list.append(prev_space)

				idx += 1


		# now another calculation for space
		display_space = self.display_width * self.display_height
		final_used_space = []

		ave_space =0 
		max_space = 0 
		temp_space = (0, self.display_width, 0, self.display_height)
		for space in space_used :
			space_list = space[1] 
			area = self._calcUsedSpace(space_list)
			area = (1.0 * area) / display_space
			temp = (space[0], area)
			final_used_space.append(temp)
			ave_space += area
			if area > max_space : 
				max_space = area

		ave_space =  1.0 * ave_space / len(space_used)
		#graph_height = window_height -61 - 50
		# hyejung
		graph_height = (window_height -61 - 50)/2

		# calcurate SD 
		sd = 0
		for space in final_used_space: 
			temp = (space[1] - ave_space) * (space[1] - ave_space)
			sd += temp
		sd = 1.0 * sd / len(final_used_space)
		sd = math.sqrt(sd)

		# drawing start from here..
		dc.SetPen(wx.Pen(foreground, 1))
		y = y_start + window_height -61
		dc.DrawLines(((x, y), (endx+right_gap, y)))

		# second line 
		y1 = y- graph_height-50 
		dc.DrawLines(((x, y1), (endx+right_gap, y1)))
		dc.DrawText("space percentage %", window_width-150, y1+20)

		dc.SetPen(wx.Pen(foreground, 1, style=wx.DOT))
		font = wx.Font(12, wx.TELETYPE, wx.NORMAL, wx.BOLD)
		dc.SetFont(font)
		for i in range(1, 10) :
			height = y - int(graph_height * 0.1*i)
			dc.DrawLines(((x, height), (endx+right_gap, height)))

		dc.SetPen(wx.Pen(foreground, 1))
		height = y - int(graph_height * ave_space)
		dc.DrawLines(((x, height), (endx+right_gap, height)))
		dc.DrawText(str(int(ave_space*100)), endx+right_gap-20, height-15)
		height = y - int(graph_height * max_space)
		dc.DrawLines(((x, height), (endx+right_gap, height)))
		dc.DrawText(str(int(max_space*100)), endx+right_gap-20, height-15)
		dc.DrawText("sd="+ str(int(sd*100)), endx+right_gap-60, height-25)

		dc.SetPen(wx.Pen(foreground, 1, style=wx.DOT))
		# for second graph
		unit_x = graph_width / 10
		x1 = x
		for i in range(10) :
			x1 += unit_x
			dc.DrawLines(((x1, y1),  (x1, 50)))
			dc.DrawText(str((i+1)*10), x1-5, y1+5)
		x1 = x
		# -----------------------

		# adjust base_unit
		base_unit = int((end - start) / 5.0)
		tmp = (end - start) % 5
		if tmp > 1 : 
			base_unit  += 1

		# draw ruler for x and y 
		dc.DrawText(str(start), x-7, y+10)
		unit_x = (end - start) / (base_unit *1.0)
		if unit_x < 1 : 
			unit_x =1
		width_x = graph_width / unit_x
		time_unit = start 
		unit_x = int(unit_x) 
		top_line = graph_height + 50
		for i in range(unit_x):
			time_unit += base_unit 
			if time_unit > end :
				tmp = base_unit - time_unit + end 
				x +=  (width_x * tmp / base_unit)
				dc.DrawLines(((x, top_line), (x, y+5)))
				dc.DrawText(str(end), x-7, y+10)
				break
			x += width_x
			dc.DrawLines(((x,  y_start + top_line), (x, y+5)))
			dc.DrawText(str(time_unit), x-7, y+10)

		width_x = width_x / (base_unit * 1.0) 
		# draw lines...
		color = self.parent.getColor(8)
		dc.SetPen(wx.Pen(color, 1))
		dc.SetBrush(wx.Brush(color))
		prev_space = None 
		for space in final_used_space :
			if prev_space != None :
				temp_l = 1.0 * prev_space[0] / 60
				left =((temp_l - start) * width_x) + origin_x 
				temp_r = 1.0 * space[0] / 60
				right =((temp_r - start) * width_x) + origin_x 
				left = int(left)
				right = int(right)

				height = int(graph_height * prev_space[1])
				width = right - left
				dc.DrawRectangle(left, y-height, width, height)
			prev_space = space 

		# -----------------------
		ave_space_data = []
		for i in range(10) : 
			ave_space_data.append(0)
		max_space = 0
		prev_space = None 
		#print final_used_space
		for space in final_used_space :
			if prev_space != None :
				#left = int(1.0 * prev_space[0]/ 10)
				#right = int(1.0 * space[0] / 10)
				left = int(prev_space[0])
				right = int(space[0])

				#print "-"
				if prev_space[1] > 0.0 : 
					i = int(prev_space[1] * 100) / 10
					if left == right : 
						ave_space_data[i] += 1
						#print left 
					else : 
						for j in range(left, right):
							ave_space_data[i] += 1
							#print j
					if ave_space_data[i] > max_space : 
						max_space = ave_space_data[i]
					#print i, "(", left, right, ")" 
			prev_space = space 

		unit_x = graph_width / 10
		y1 = y- graph_height-50 
		half_x_unit = unit_x / 2
		unit_y = 1.0 * (graph_height - 50) / max_space
		color = self.parent.getColor(9)
		dc.SetPen(wx.Pen(color, 1))
		dc.SetBrush(wx.Brush(color))
		for w in ave_space_data:
			h = int(w * unit_y)  
			dc.DrawRectangle(x1, y1-h, unit_x, h)
			dc.DrawText(str(w), x1+half_x_unit, y1-h-15)
			x1 += unit_x
		del ave_space_data
		# -----------------------

		for space in final_used_space :
			del space
		del final_used_space

		for space in space_used :
			space_list = space[1] 
			del space_list
			del space
		del space_used


	def _calcUsedSpace(self, space_list):
		temp_space = (0, self.display_width, 0, self.display_height)
		self.largestEmptySpaceList = []
       		self.largestEmptySpaceList.append(temp_space)

		#print "calcUsedSpace started------------------->"
		for space in space_list :
			self.__addFullRectangle(space)

		# re-adjust is required.... 
		self.overlapped_list  = []
		index =0
		for space in self.largestEmptySpaceList :
			self.__getOverlapped(space, index)
			index += 1
		#print self.overlapped_list

		temp_list = []
		total_empty_space = 0
		index =0
		for space in self.largestEmptySpaceList :
			width = space[1] - space[0]
			height = space[3] - space[2]
			area = width * height

			overlapped_area = 0 
			o_area_list = self.overlapped_list[index]	
			o_areas = o_area_list[1]
			for O in o_areas:
				overlapped_area += O[1]
				if index < O[0] : 
					temp = index, O[0], O[1]
				else : 
					temp = O[0], index, O[1]
				# search for same set
				if not(temp in temp_list) : 
					temp_list.append(temp)
			area = area - overlapped_area
			total_empty_space += area 
			index += 1

		overlapped_area = 0 
		for T in temp_list:
			overlapped_area += T[2]
		total_empty_space += overlapped_area 

		if total_empty_space == 0 : 
			print "empty.... "
			print "-------- calcUsedSpace: ", self.largestEmptySpaceList
			print space_list 

		display_space = self.display_width * self.display_height
		used_space = (display_space - total_empty_space) 

		# strange error...
		'''
		if used_space > display_space :
			print "used_space =", used_space, "overlapped_area =", overlapped_area, "display_space=", display_space 
			print self.overlapped_list
			print temp_list
		'''

		if used_space < 0 : 
			print "negative.... "
			print space_list 


		for space in self.largestEmptySpaceList :
			del space
		del self.largestEmptySpaceList
		self.largestEmptySpaceList = []
		del temp_list 
		del self.overlapped_list
		self.overlapped_list = []

		#print "calcUsedSpace <------------------- end"
		if used_space > display_space :
			print "[error] calcUsedSpace: used space was greater then display space, used_space=", used_space, "display_space=", display_space
                        return display_space-1
			#return 0

		return used_space 


	def __getOverlapped(self, P, i):
		j =0
		temp_list = []
		for space in self.largestEmptySpaceList :
			if i == j: 
				j += 1 
				continue 

			# check overlapped or not
			if not (space[1] < P[0] or space[0] > P[1] or space[3] < P[2] or space[2] > P[3]) : 
				# get area 
				if P[0] <= space[0]:
					left = space[0] 
 					if P[1] <= space[1]:
						right = P[1] 
					else:
						right = space[1] 
				else :
					left = P[0] 
 					if P[1] >= space[1]:
						right = space[1] 
					else: 
						right = P[1] 
				if P[2] <= space[2]:
					bottom = space[2] 
 					if P[3] <= space[3]:
						top= P[3] 
					else: 
						top= space[3] 
				else :
					bottom = P[2] 
 					if P[3] >= space[3]:
						top= space[3] 
					else : 
						top= P[3] 
				#print "overlapped area=> ", j, left, right, bottom, top
				width = right - left
				height = top - bottom
				area = width * height
				temp = j, area
				temp_list.append(temp)
			j += 1
		temp = i, temp_list
		self.overlapped_list.append(temp)
		del temp_list 



	def __getNotOverlapped(self, P, i, possibleLESList):
		#print i, P, possibleLESList
		j = 0
		isIn = False
		for possible in possibleLESList :
			if i != j :
				if possible[0] <= P[0] and P[1] <= possible[1] and possible[2] <= P[2] and P[3] <= possible[3] :
					isIn = True 
					break
			j += 1

		l = []
		if isIn == False : 
			overlapped = False
			j = 0 
			for possible in possibleLESList :
				if i == j :
					continue
				if not (possible[1] < P[0] or possible[0] > P[1] or possible[3] < P[2] or possible[2] > P[3]) : 
					# if space is are overlapped. 
					if P[0] >= possible[0] and P[0] < possible[1] :
						if P[3] >= possible[2] and P[3] < possible[3] :
							temp = (P[0], possible[1], P[2], possible[2])
 							if self.__validate(temp) :
								l.append(temp)
							temp = (possible[1], P[1], P[2], P[3])
 							if self.__validate(temp) :
								l.append(temp)
						else :
							temp = (P[0], possible[1], possible[3], P[2])
 							if self.__validate(temp) :
								l.append(temp)
							temp = (possible[1], P[1], P[2], P[3])
 							if self.__validate(temp) :
								l.append(temp)
					else : 
						if P[3] >= possible[2] and P[3] < possible[3] :
							temp = (P[0], possible[1], P[2], P[3])
 							if self.__validate(temp) :
								l.append(temp)
							temp = (possible[0], P[1], P[2], possible[2])
 							if self.__validate(temp) :
								l.append(temp)
						else :
							temp = (P[0], possible[1], P[2], P[3])
 							if self.__validate(temp) :
								l.append(temp)
							temp = (possible[0], P[1], possible[3], P[3])
 							if self.__validate(temp) :
								l.append(temp)
					overlapped = True
					break
			if overlapped == False : 
				l.append(P)
			j += 1
		return  l


	def __addFullRectangle(self, app):
		#print "<", app, ">"

		# have list existing rectangles in largestEmptySpaceList that intersect or are adjacent to current app
		cList = []
		index = 0
		removeList = []
		#  id(-1) left, right, bottom, top
		for space in self.largestEmptySpaceList :
			if not (space[1] <= app[1] or space[0] >= app[2] or space[3] <= app[3] or space[2] >= app[4]) : 
				#print space, "***in"
				cList.append((space[0], space[1], space[2], space[3]))
				removeList.append(index)
			index += 1
		index = 0
		for i in removeList : 
			self.largestEmptySpaceList.pop(i-index)
			index += 1

		adjacentLESList = []
		possibleLESList_left = []
		possibleLESList_right = []
		possibleLESList_bottom = []
		possibleLESList_top = []

		for O in cList : 
			# current app  = F
			# if O adjacent to any side e of F and external to F
			if app[1] == O[1] or app[2] == O[0] or app[4] == O[2] or app[3] == O[3] :  
				# ?????? correct or not????
				adjacentLESList.append(O)
			else : 
				if O[0] < app[1] :      # top left
					possibleLESList_left.append((O[0], app[1], O[2], O[3]))
				if O[1] > app[2] :    # bottom right
					possibleLESList_right.append((app[2], O[1], O[2], O[3]))
				if O[2] < app[3] :  # bottom left 
					possibleLESList_bottom.append((O[0], O[1], O[2], app[3]))
				if O[3] > app[4] :        # top right 
					possibleLESList_top.append((O[0], O[1], app[4], O[3]))

		index = 0 
		possibleLESList = []
		for P in possibleLESList_left : 
			if self.__isNotInClosed(P, index, possibleLESList_left, adjacentLESList) == True :
				possibleLESList.append(P)
			index += 1 

		index = 0 
		for P in possibleLESList_right : 
			if self.__isNotInClosed(P, index, possibleLESList_right, adjacentLESList) == True :
				possibleLESList.append(P)
			index += 1 

		index = 0 
		for P in possibleLESList_top : 
			if self.__isNotInClosed(P, index, possibleLESList_top, adjacentLESList) == True :
				possibleLESList.append(P)
			index += 1 

		index = 0 
		for P in possibleLESList_bottom : 
			if self.__isNotInClosed(P, index, possibleLESList_bottom, adjacentLESList) == True :
				possibleLESList.append(P)
			index += 1 


		#print "**** start add possible les list "
		index = 0
 		for P in possibleLESList : 
 			if self.__isNotInClosed(P, index, possibleLESList, None): 
 				if self.__validate(P) :
 					self.largestEmptySpaceList.append(P)
 					#print self.largestEmptySpaceList
 			index += 1

		del possibleLESList
		del possibleLESList_left
		del possibleLESList_right
		del possibleLESList_bottom
		del possibleLESList_top
		del adjacentLESList
		del removeList
		del cList 


	def __validate(self, space):
		height = space[3] - space[2] 
		if height <= 1 :
			return False
		width = space[1] - space[0] 
		if width <= 0 :
			return False

		if space[0] < 0 or space[1] < 0 or space[2] < 0 or space[3] < 0 :
			return False
		return True

	def __isNotInClosed(self, P, i, possibleLESList, adjacentLESList):
		j = 0
		for possible in possibleLESList :
			if i != j :
				if possible[0] <= P[0] and P[1] <= possible[1] and possible[2] <= P[2] and P[3] <= possible[3] :
					return False
			j += 1

		if adjacentLESList != None :
			for adjacent in adjacentLESList :
				if adjacent[0] <= P[0] and P[1] <= adjacent[1] and adjacent[2] <= P[2] and P[3] <= adjacent[3] :
					return False
		return True
